//
//  JWSentinel.m
//  KotH
//
//  Created by James Weatherley on 01/11/2009.
//  Copyright 2009 James Weatherley. All rights reserved.
//

#import "../koth.h"
#import "../wolfiphone.h"
#import "../geometry/geometry.h"
#import "../iphone/wolf3dAppDelegate.h"
#import "../iphone/EAGLView.h"
#import <QuartzCore/CADisplayLink.h>

@implementation JWSentinel

static BOOL s_resting = YES;
static NSUInteger warningMask = 0;
static NSUInteger dangerMask = 0;


@synthesize selected;
@synthesize square;
@synthesize theta;
@synthesize sentinelId;

#define SENTINEL_CAMERA_HEIGHT 1.9f

#pragma Protocol
-(id)initWithSquare:(JWSquare*)square_
{
	self = [super init];
	
	square = square_;
	[square setTree:NO];
	[square setSentinel:self];
	[self calculateSentinelGeometry];
	
	theta = 0.0f;
	canCreateMeanie = YES;
	noDamage = 0;
	
	return self;
}

-(void)dealloc
{
	[super dealloc];
	
	if(meanie) {
		meanie.master = nil;
		meanie = nil;
	} else {
		warningMask &= ~(1 << sentinelId);
		dangerMask &= ~(1 << sentinelId);
	}
}

+(void)resting:(BOOL)rest
{
	s_resting = rest;
}

+(BOOL)resting
{
	return s_resting;
}

+(NSUInteger)warningMask
{
	return warningMask;
}

+(NSUInteger)dangerMask
{
	return dangerMask;
}

+(void)clearMasks
{
	warningMask = 0;
	dangerMask = 0;
}

+(NSInteger)energy
{
	// The boss (sentinel id == 0) has energy 4, but we'll fiddle that when we absorb it.
	return 3;
}

#pragma mark Geometry

-(void)calculateSentinelGeometry
{
	// This is a bit of a mess! hat and visor rotate, so vertices are calculated for positioning
	// them at the origin. They are translated and rotated at draw time.
	// The other vertices are pre-calculated, and don't need shifting.
	GLfloat baseRadius = 0.5f;
	GLfloat topRadius = 0.25f;
	GLfloat visorRadius = 0.75f;
	GLfloat angle = 0.0f;
	GLfloat bodyHeight = 1.8f;
	GLfloat totalHeight = 2.0f;
	
	float xBase, zBase, xTop, zTop, xVisor, zVisor;
	for(NSUInteger i = 0; i < 4; ++i) {
		xBase = baseRadius * sin(angle);
		zBase = baseRadius * cos(angle);
		xTop = topRadius * sin(angle);
		zTop = topRadius * cos(angle);
		xVisor = visorRadius * sin(angle - (M_PI / 4.0f));
		zVisor = visorRadius * cos(angle - (M_PI / 4.0f));
		
		bodyVertices[i][0] = [square x] + 0.5f + xBase;
		bodyVertices[i][1] = [square height] + [[square boxes] count];
		bodyVertices[i][2] = [square z] + 0.5f + zBase;
		bodyVertices[i + 4][0] = [square x] + 0.5f + xTop;
		bodyVertices[i + 4][1] = [square height] + [[square boxes] count] + bodyHeight;
		bodyVertices[i + 4][2] = [square z] + 0.5f + zTop;
		
		headVertices[i][0] = xVisor;
		headVertices[i][1] = bodyHeight;
		headVertices[i][2] = zVisor;
		headVertices[i + 4][0] = xVisor;
		headVertices[i + 4][1] = totalHeight;
		headVertices[i + 4][2] = zVisor;
		
		if(i == 1) {
			visorRadius /= 2.0f;
		}
		
		angle += M_PI / 2;
	}
	
	calculateNormal(bodyVertices[0], bodyVertices[1], bodyVertices[4], bodyNormals[0]);
	calculateNormal(bodyVertices[1], bodyVertices[2], bodyVertices[5], bodyNormals[1]);
	calculateNormal(bodyVertices[2], bodyVertices[3], bodyVertices[6], bodyNormals[2]);
	calculateNormal(bodyVertices[3], bodyVertices[0], bodyVertices[7], bodyNormals[3]);
	
	calculateNormal(headVertices[0], headVertices[1], headVertices[4], headNormals[0]);
	calculateNormal(headVertices[1], headVertices[2], headVertices[5], headNormals[1]);
	calculateNormal(headVertices[2], headVertices[3], headVertices[6], headNormals[2]);
	calculateNormal(headVertices[0], headVertices[1], headVertices[2], headNormals[4]);
	calculateNormal(headVertices[4], headVertices[5], headVertices[6], headNormals[5]);
}

-(vec3_t*)bodyNormals
{
	return bodyNormals;
}

-(vec3_t*)bodyVertices
{
	return bodyVertices;
}

-(vec3_t*)headNormals
{
	return headNormals;
}

-(vec3_t*)headVertices
{
	return headVertices;
}

-(NSObject<JWManager>*)manager
{
	return [[JWManagers sharedManager]managerOfClass:[JWSentinelManager class]];
}

#pragma mark Implementation
-(void)doWork
{
	if(s_resting) {
		return;
	}
	
	if(meanie) {
		[meanie doWork];
		return;
	}
	
	JWPicker* picker = [JWPicker picker];
	
	vec3_t ray;
	vec3_t point;
	
	ray[0] = sin(DEG2RAD(theta));
	ray[1] = 0;
	ray[2] = cos(DEG2RAD(theta));
	
	point[0] = square.x + 0.5f;
	point[1] = square.height + SENTINEL_CAMERA_HEIGHT;
	point[2] = square.z + 0.5f;
	
	float angleToPlayer = [self checkPlayer];
	
	if(!canCreateMeanie && angleToPlayer > kothSentinelFOV) {
		canCreateMeanie = YES;
	}
	
	NSArray* squares = [picker squaresInPickViewAlong:ray from:point];
	JWSquare* s = nil;
	for(s in squares) {
		NSUInteger boxes = [s.boxes count];
		if(s.player || s.robot || boxes) {
			if([self squareVisible:s]) {
				break;
			}
		}
	}

	if(s) {
		// When going from box to tree we want to plant the tree after removing the box.
		// Otherwise we get a floating tree...
		BOOL box2tree = NO;
		
		dangerMask &= ~(1 << sentinelId);
		
		NSObject<JWEntity>* item = nil;
		if(s.tree) {
			//NSLog(@"Found Tree!");
			item = s.tree;
		} else if(s.robot) {
			//NSLog(@"Found Robot!");
			item = s.robot;
			JWBox* box = [[[JWBox alloc] initWithSquare:s] autorelease];
			[[box manager] addEntity:box];
		} else if(s.player) {
			//NSLog(@"Found Player!");
			
			if(angleToPlayer < kothSentinelFOV) {
				
				wolf3dAppDelegate* appDelegate = (wolf3dAppDelegate*)[UIApplication sharedApplication].delegate;
				if(!noDamage) {
					--Player.health;
					if(Player.health < 0) {
						Player.health = -1;
					}
					
					NSInteger frameInterval = appDelegate.glView.displayLink.frameInterval;
					CFTimeInterval duration = appDelegate.glView.displayLink.duration;
					noDamage = kothSentinelAbsorbRate / (frameInterval * duration) / MAX_SENTINELS;
				} else {
					--noDamage;
				}
			
				dangerMask |= 1 << sentinelId;
				[appDelegate updateHealthDisplay];
			} else {
				s = nil;
				noDamage = 0;
			}
		} else if([s.boxes count]) {
			//NSLog(@"Found Boxes!");
			item = s.topBox;
			box2tree = YES;
		} else {
			//NSLog(@"Found nothing of interest!");
			s = nil;
		}
		if(item) {
			[[item manager] removeEntity:item];
			[picker.landscape plantTree];
			if(box2tree) {
				JWTree* tree = [[[JWTree alloc] initWithSquare:s] autorelease];
				[[tree manager] addEntity:tree];
			}
		}
	}
	
	if(!s) {
		noDamage = 0;
		if(angleToPlayer < kothMeanieAngle && canCreateMeanie) {
			if(![self createMeanie]) {
				canCreateMeanie = NO;
			}
		} else {
			theta += kothSentinelRotationSpeed;
			theta = FINE2DEG(NormalizeAngle(DEG2FINE(theta)));
			//NSLog(@"%@: %f", @"Rotating - theta = ", theta);
		}
	}
}

-(float)checkPlayer
{
	float deltaX = square.x - Player.position.origin[0];
	float deltaZ = square.z - -Player.position.origin[1];
	
	// Not sure why the M_PI is needed, but it is.
	float angleToPlayer = NormalizeAngle(RAD2DEG(atan2(deltaX, deltaZ) + M_PI));
	angleToPlayer = fabs(angleToPlayer - theta);
	
	// Don't let meanies mess with the masks.
	if(sentinelId != 666) {
		if(angleToPlayer < kothSentinelWarning) {
			warningMask |= 1 << sentinelId;
		} else {
			warningMask &= ~(1 << sentinelId);
		}
	}
	return angleToPlayer;
}

-(BOOL)squareVisible:(JWSquare*)s
{
	vec3_t point;
	vec3_t ray;
	vec3_t squareCentre;
	
	point[0] = square.x + 0.5f;
	point[1] = square.height + SENTINEL_CAMERA_HEIGHT;
	point[2] = square.z + 0.5f;
	
	squareCentre[0] = s.x + 0.5f;
	squareCentre[1] = s.height;
	squareCentre[2] = s.z + 0.5f;
	vectorSubtract(squareCentre, point, ray);
	_VectorNormalize(ray);
	
	JWSquare* pickedSquare = [[JWPicker picker] pickUsingWorldCoordinatesAlong:ray from:point];
	
	/*			
	// Test the top of the boxes too.
	if(pickedSquare != s) {
		if([s.boxes count]) {
			squareCentre[0] = s.x + 0.5f;
			squareCentre[1] = s.height + boxes;
			squareCentre[2] = s.z + 0.5f;
			vectorSubtract(squareCentre, point, ray);
			_VectorNormalize(ray);
			
			JWSquare* pickedSquare = [picker pickUsingWorldCoordinatesAlong:ray from:point];
		}
	}
	*/
				 
	return (pickedSquare == s);
}
		

#pragma mark Meanie
-(BOOL)createMeanie
{
	assert(!meanie);
	assert(canCreateMeanie);
	
	JWLandscapeManager* landscapeManager = (JWLandscapeManager*)[[JWManagers sharedManager] managerOfClass:[JWLandscapeManager class]];
	JWLandscape* landscape = (JWLandscape*)[landscapeManager entity];
	
	JWSquare* treeSquare = nil;
	NSArray* treeSquares = [landscape getTreeSquaresNearPlayer];
	NSUInteger count = [treeSquares count];
	
	if(count) {
		// Favour higher squares, as they're worse for the player.
		for(NSInteger i = count - 1; i >= 0; --i) {
			treeSquare = [treeSquares objectAtIndex:i];
			
			if([self squareVisible:treeSquare]) {
				break;
			} else {
				treeSquare = nil;
			}
		}
	
		if(treeSquare) {
			JWTree* tree = treeSquare.tree;
			treeSquare.tree = nil;
			treeSquare.sentinel = self;
			[[tree manager] removeEntity:tree];
			
			meanie = [[[JWMeanie alloc] initWithSquare:treeSquare] autorelease];
			meanie.sentinelId = 666;
			meanie.master = self;
			
			JWSentinelManager* sentinelManager = (JWSentinelManager*)[[JWManagers sharedManager] managerOfClass:[JWSentinelManager class]];
			[sentinelManager addEntity:meanie];
		}
	}
	
	return !!meanie;
}

-(void)meanieFinished
{
	meanie = nil;
	canCreateMeanie = NO;
}

@end
